package com.communal.service.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.communal.dao.ICommunalDao;
import com.communal.dao.IModelDao;
import com.communal.model.Model;
import com.communal.model.cr.CRElectricity;
import com.communal.model.cr.CRHeating;
import com.communal.model.cr.CRIntercom;
import com.communal.model.cr.CRInternet;
import com.communal.model.cr.CRRent;
import com.communal.model.cr.CRWater;
import com.communal.service.IModelService;
import com.communal.util.AppException;
import com.communal.util.CommunalFilterBean;
import com.communal.web.form.cr.CRGeneralForm;

/**
 * Implements business logic operations for model <br>
 * @author Created by andrey.kuznetsov <br>
 * @author Last modified by $Author$ <br>
 * @author Last modified on $Date$ at revision $Revision$ <br>
 */
@Service
public class ModelService extends AbstractService implements IModelService{
	
	@Autowired
	IModelDao modelDao;

	@Autowired
	ICommunalDao communalDao;
	
	@Override
	@Transactional(readOnly=true)
	public List<CRGeneralForm> getTotalInformation(Model p_model, CommunalFilterBean p_filter) throws AppException {
		List<CRGeneralForm> formList = new ArrayList<CRGeneralForm>();
		
		try{
			checkModel( p_model );
			Set<Integer> monthYearKeySet = new TreeSet<Integer>();
			
			Map<Integer, Short> mapMonth = new HashMap<Integer, Short>();
			Map<Integer, Short> mapYear = new HashMap<Integer, Short>();
			
			//get rent data
			Map<Integer, Double> mapRent = new HashMap<Integer, Double>();
			List<CRRent> rentRecords = communalDao.getRecords( p_model, CRRent.class, p_filter );
			if(CollectionUtils.isNotEmpty( rentRecords )){
				for(CRRent record: rentRecords){
					Integer key = null;
					try{
						key = generateKey( record.getMonth(), record.getYear() );
					}catch (NumberFormatException err) {
						continue;
					} 
					mapRent.put( key , record.getSumma() );
					mapMonth.put( key, record.getMonth() );
					mapYear.put( key , record.getYear() );
				}
				monthYearKeySet.addAll( mapRent.keySet() );
			}
			
			//get electricity data
			Map<Integer, Double> mapElectricity = new HashMap<Integer, Double>();
			List<CRElectricity> electricityRecords = communalDao.getRecords( p_model, CRElectricity.class, p_filter );
			if(CollectionUtils.isNotEmpty( electricityRecords )){
				for(CRElectricity record: electricityRecords){
					Integer key = null;
					try{
						key = generateKey( record.getMonth(), record.getYear() );
					}catch (NumberFormatException err) {
						continue;
					} 
					mapElectricity.put( key, record.getSumma() );
					mapMonth.put( key, record.getMonth() );
					mapYear.put( key, record.getYear() );
				}
				monthYearKeySet.addAll( mapElectricity.keySet() );
			}
			
			//get water data
			Map<Integer, Double> mapColdWater = new HashMap<Integer, Double>();
			Map<Integer, Double> mapHotWater = new HashMap<Integer, Double>();
			List<CRWater> waterRecords = communalDao.getRecords( p_model, CRWater.class, p_filter );
			if(CollectionUtils.isNotEmpty( waterRecords )){
				for(CRWater record: waterRecords){
					Integer key = null;
					try{
						key = generateKey( record.getMonth(), record.getYear() );
					}catch (NumberFormatException err) {
						continue;
					} 
					mapColdWater.put( key, record.getSummaCold() );
					mapHotWater.put( key, record.getSummaHot() );
					mapMonth.put( key, record.getMonth() );
					mapYear.put( key, record.getYear() );
				}
				monthYearKeySet.addAll( mapColdWater.keySet() );
				monthYearKeySet.addAll( mapHotWater.keySet() );
			}
			
			//get heating data
			Map<Integer, Double> mapHeating = new HashMap<Integer, Double>();
			List<CRHeating> heatingRecords = communalDao.getRecords( p_model, CRHeating.class, p_filter );
			if(CollectionUtils.isNotEmpty( heatingRecords )){
				for(CRHeating record: heatingRecords){
					Integer key = null;
					try{
						key = generateKey( record.getMonth(), record.getYear() );
					}catch (NumberFormatException err) {
						continue;
					} 
					mapHeating.put( key, record.getSumma() );
					mapMonth.put( key, record.getMonth() );
					mapYear.put( key, record.getYear() );
				}
				monthYearKeySet.addAll( mapHeating.keySet() );
			}
			
			//get intercom data
			Map<Integer, Double> mapIntercom = new HashMap<Integer, Double>();
			List<CRIntercom> intercomRecords = communalDao.getRecords( p_model, CRIntercom.class, p_filter );
			if(CollectionUtils.isNotEmpty( intercomRecords )){
				for(CRIntercom record: intercomRecords){
					Integer key = null;
					try{
						key = generateKey( record.getMonth(), record.getYear() );
					}catch (NumberFormatException err) {
						continue;
					} 
					mapIntercom.put( key, record.getSumma() );
					mapMonth.put( key, record.getMonth() );
					mapYear.put( key, record.getYear() );
				}
				monthYearKeySet.addAll( mapIntercom.keySet() );
			}
			
			//get internet data
			Map<Integer, Double> mapInternet = new HashMap<Integer, Double>();
			List<CRInternet> internetRecords = communalDao.getRecords( p_model, CRInternet.class, p_filter );
			if(CollectionUtils.isNotEmpty( internetRecords )){
				for(CRInternet record: internetRecords){
					Integer key = null;
					try{
						key = generateKey( record.getMonth(), record.getYear() );
					}catch (NumberFormatException err) {
						continue;
					} 
					mapInternet.put( key, record.getSumma() + record.getAdvancedSumma() );
					mapMonth.put( key, record.getMonth() );
					mapYear.put( key, record.getYear() );
				}
				monthYearKeySet.addAll( mapInternet.keySet() );
			}
			
			if(CollectionUtils.isNotEmpty( monthYearKeySet )){
				CRGeneralForm form = null;
				for(Integer key: monthYearKeySet){
					form = new CRGeneralForm();
					form.setId( key );
					form.setMonth( (short) ( mapMonth.containsKey( key ) ? mapMonth.get( key ) : 0d ) );
					form.setYear( (short) ( mapYear.containsKey( key ) ? mapYear.get( key ) : 0d ) );
					form.setSummaRent( mapRent.containsKey( key ) ? mapRent.get( key ) : 0d );
					form.setSummaElectricity( mapElectricity.containsKey( key ) ? mapElectricity.get( key ) : 0d );
					form.setSummaColdWater( mapColdWater.containsKey( key ) ? mapColdWater.get( key ) : 0d );
					form.setSummaHotWater( mapHotWater.containsKey( key ) ? mapHotWater.get( key ) : 0d );
					form.setSummaGas( 0d ); //TODO change after gas tab implementing
					form.setSummaHeating( mapHeating.containsKey( key ) ? mapHeating.get( key ) : 0d );
					form.setSummaPhone( 0d ); //TODO change after phone tab implementing
					form.setSummaIntercom( mapIntercom.containsKey( key ) ? mapIntercom.get( key ) : 0d );
					form.setSummaInternet( mapInternet.containsKey( key ) ? mapInternet.get( key ) : 0d );
					form.setSummaTv( 0d ); //TODO change after tv tab implementing
					calculateTotal( form );
					formList.add( form );
				}
			}
		}
		catch (Exception err) {
			log.error(messageSource.getMessage("log.error.load", new Object[]{err.getMessage(), "total information"}, null));
			throw new AppException(messageSource.getMessage("message.error.common", null, null));
		}
		return formList;
	}
	
	/**
	 * Generates number key from year and month <br>
	 * @param p_month - number of month; {@link Short} <br>
	 * @param p_year - number of year; {@link Short} <br>
	 * @return unique number key; {@link Integer}
	 */
	private Integer generateKey(Short p_month, Short p_year) throws NumberFormatException{
		if(p_month == null || p_year == null) 
			return 0;
		return Integer.parseInt(p_year.toString()+p_month.toString());
	}
	
	/**
	 * Calculates total summa for payment and set it to corresponding field <br>
	 * @param p_form form; {@link CRGeneralForm}
	 */
	private void calculateTotal(CRGeneralForm p_form){
		Double summa = p_form.getSummaRent() + p_form.getSummaElectricity() +
		p_form.getSummaColdWater() + p_form.getSummaHotWater() + p_form.getSummaGas() +
		p_form.getSummaHeating() + p_form.getSummaPhone()+ p_form.getSummaIntercom() + 
		p_form.getSummaInternet() + p_form.getSummaTv();
		p_form.setSummaTotal( summa );
	}
}
